package com.geor.gcv.core.mat;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import javax.imageio.ImageIO;

import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

import com.geor.gcv.core.model.Area;
import com.geor.gcv.core.model.Node;
import com.geor.gcv.core.process.GrayProcess;
import com.geor.gcv.core.process.InputProcess;
import com.geor.gcv.core.process.ResizeProcessAny;
import com.geor.gcv.core.process.ScreenShootProcess;
import com.geor.gcv.core.process.ThresholdProcess;

/**
 * 图像基础函数
 * 
 * @author geor.lusongnan
 *
 */
public abstract class Mats {
	public static String ROOT = System.getProperty("user.dir");

	public final static int PEX_WHITE = 255;
	public final static int PEX_BLACK = 0;

	/**
	 * 根据区域集合在图片上画框
	 * 
	 * @param mat
	 * @param areas
	 */
	public static void print(Mat mat, List<Area> areas) {
		for (Area area : areas) {
			int x = area.getX();
			int y = area.getY();
			int width = area.getWidth();
			int height = area.getHeight();
			for (int i = x; i < x + width; i++) {
				mat.put(y, i, 0, 0, 255);
				mat.put(y + height, i, 0, 0, 255);
			}

			for (int i = y; i < y + height; i++) {
				mat.put(i, x, 0, 0, 255);
				mat.put(i, x + width, 0, 0, 255);
			}
		}
	}

	/**
	 * 生成1*x像素图像
	 * 
	 * @param mat
	 * @return
	 */
	public static Mat genSimpleMat(Mat mat) {
		Mat sample = new Mat(1, mat.rows() * mat.cols(), CvType.CV_32FC1);
		int j = 0;
		for (int n = 0; n < mat.rows(); n++) {
			for (int m = 0; m < mat.cols(); m++) {
				sample.put(0, j++, mat.get(n, m)[0]);
			}
		}
		return sample;
	}

	public static float[] genSimpleMatArray(Mat mat) {
		float temps[] = new float[mat.rows() * mat.cols()];
		int j = 0;
		for (int n = 0; n < mat.rows(); n++) {
			for (int m = 0; m < mat.cols(); m++) {
				temps[j++] = (float) mat.get(n, m)[0];
			}
		}
		return temps;
	}

	/**
	 * 切分行
	 * 
	 * @param step
	 * @return
	 */
	public static List<Integer> getRows(Mat step) {
		return getRows(step, 0.1, 0.95);
	}

	/**
	 * 切分行
	 * 
	 * @param step
	 * @param lt
	 *            左边忽略比例
	 * @param rt
	 *            右边忽略比例
	 * @return
	 */
	public static List<Integer> getRows(Mat step, double lt, double rt) {
		List<Integer> rows = new ArrayList<Integer>();
		int preRowColor = 1;
		int curRowColor = 1;
		for (int i = 0; i < step.rows(); i++) {
			curRowColor = 1;
			for (int j = (int) (step.cols() * lt); j < (int) (step.cols() * rt); j++) {
				double pex = step.get(i, j)[0];
				if (pex > 100) {
					curRowColor = 0;
				}
			}

			if (curRowColor != preRowColor) {
				rows.add(i);
			}
			preRowColor = curRowColor;
		}

		return rows;
	}

	/**
	 * 根据行号切分列
	 * 
	 * @param step
	 * @param rows
	 * @return
	 */
	public static List<Integer> getCols(Mat step, List<Integer> rows) {
		int preColColor = 1;
		int curColColor = 1;
		int start = 0;
		int end = step.rows();

		if (rows != null && rows.size() >= 2) {
			start = rows.get(0);
			end = rows.get(rows.size() - 1);
		}

		List<Integer> cols = new ArrayList<Integer>();
		for (int col = 0; col < step.cols(); col++) {
			curColColor = 1;
			for (int row = start; row < end; row++) {
				double pex = step.get(row, col)[0];
				if (pex > 100) {
					curColColor = 0;
				}
			}

			if (curColColor != preColColor) {
				if (col != 0 && col + 1 != cols.size()) {
					cols.add(col);
				}
			}
			preColColor = curColColor;
		}

		return cols;
	}

	/**
	 * BufferedImage转Mat
	 * 
	 * @param bufImg
	 * @param imgType
	 *            bufferedImage的类型 如 BufferedImage.TYPE_3BYTE_BGR
	 * @param matType
	 *            转换成mat的type 如 CvType.CV_8UC3
	 * @return
	 */
	public static Mat BufImgToMat(BufferedImage bufImg, int imgType, int matType) {
		if (bufImg == null) {
			throw new IllegalArgumentException("bufImg == null");
		}

		if (bufImg.getType() != imgType) {

			BufferedImage image = new BufferedImage(bufImg.getWidth(), bufImg.getHeight(), imgType);

			Graphics2D g = image.createGraphics();
			try {
				g.setComposite(AlphaComposite.Src);
				g.drawImage(bufImg, 0, 0, null);
			} finally {
				g.dispose();
			}
		}

		byte[] pixels = ((DataBufferByte) bufImg.getRaster().getDataBuffer()).getData();
		Mat mat = Mat.eye(bufImg.getHeight(), bufImg.getWidth(), matType);
		mat.put(0, 0, pixels);
		return mat;
	}

	public static Mat BufImgToMat(BufferedImage bugImg) {
		return BufImgToMat(bugImg, BufferedImage.TYPE_BYTE_GRAY, CvType.CV_8UC1);
	}

	public static byte[] MatToByte(Mat mat, String fileExten) {
		MatOfByte mob = new MatOfByte();

		Highgui.imencode(fileExten, mat, mob);
		byte[] byteArray = mob.toArray();

		Mats.releseMat(mob);

		return byteArray;
	}

	public static byte[] MatToByte(Mat mat) {
		return MatToByte(mat, ".png");
	}

	/**
	 * mat转image
	 * 
	 * @param mat
	 * @param fileExten
	 * @return
	 */
	public static BufferedImage MatToImage(Mat mat, String fileExten) {
		byte[] byteArray = MatToByte(mat, fileExten);
		return ByteToBufferedImage(byteArray);
	}

	/**
	 * Bytes转bufferedImage
	 * 
	 * @param bytes
	 * @return
	 */
	public static BufferedImage ByteToBufferedImage(byte bytes[]) {
		BufferedImage bufImage = null;
		try {
			InputStream in = new ByteArrayInputStream(bytes);
			bufImage = ImageIO.read(in);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bufImage;
	}

	public static Mat ByteToMat(byte[] datas) {
		return BufImgToMat(ByteToBufferedImage(datas));
	}

	/**
	 * 给图片1像素黑边
	 * 
	 * @param img
	 * @return
	 */
	public static Mat clearBorder(Mat img) {
		return clearBorder(img, 1);
	}

	public static Mat clearBorder(Mat img, int pex) {
		if (img.height() == 1 || img.width() == 1) {
			return img;
		}
		if (img != null) {
			img.rowRange(0, pex).setTo(new Scalar(PEX_BLACK));
			img.rowRange(img.rows() - pex, img.rows()).setTo(new Scalar(PEX_BLACK));
			img.colRange(0, pex).setTo(new Scalar(PEX_BLACK));
			img.colRange(img.cols() - pex, img.cols()).setTo(new Scalar(PEX_BLACK));
		}
		return img;
	}

	/**
	 * 去除黑边
	 * 
	 * @param mat
	 * @return
	 */
	public static Mat removeTheInterference(Mat mat) {
		if (mat != null) {
			mat = clearBlackL(mat, 1);
			mat = clearBlackD(mat, 1);
			mat = clearBlackU(mat, 1);
			mat = clearBlackR(mat, 1);
		}
		return mat;
	}

	/**
	 * 去图片 下面黑色部分
	 * 
	 * @param src
	 * @return
	 */
	public static Mat clearBlackD(Mat src, int min) {
		boolean flag = false;
		for (int row = src.rows() - 1; row >= 0; row--) {
			flag = false;
			int index = 0;
			for (int col = 0; col < src.cols(); col++) {
				double px = src.get(row, col)[0];
				if (px > 0) {
					index++;
					if (index == min) {
						flag = true;
						break;
					}
				}
			}
			if (flag) {
				if (row < (int) src.size().height - 2)
					src = new ScreenShootProcess(new InputProcess(src), new Area(0, 0, (int) src.size().width, row + 2))
							.process();
				break;
			}
		}
		return src;
	}

	/**
	 * 去图片 上部黑色部分
	 * 
	 * @param src
	 * @return
	 */
	public static Mat clearBlackU(Mat src, int min) {
		boolean flag = false;
		for (int row = 0; row < src.rows(); row++) {
			flag = false;
			int index = 0;
			for (int col = 0; col < src.cols(); col++) {
				double px = src.get(row, col)[0];
				if (px > 0) {
					index++;
					if (index == min) {
						flag = true;
						break;
					}
				}
			}
			if (flag) {
				if (row > 2)
					src = new ScreenShootProcess(new InputProcess(src),
							new Area(0, row - 1, (int) src.size().width, (int) src.size().height - (row - 1)))
									.process();
				break;
			}
		}
		return src;
	}

	/**
	 * 去图片 左边黑色部分
	 * 
	 * @param src
	 * @return
	 */
	public static Mat clearBlackL(Mat src, int min) {
		boolean flag = false;
		for (int col = 0; col < src.cols(); col++) {
			flag = false;
			int index = 0;
			for (int row = 0; row < src.rows(); row++) {
				double px = src.get(row, col)[0];
				if (px > 0) {
					index++;
					if (index == min) {
						flag = true;
						break;
					}
				}
			}
			if (flag) {
				if (col >= 2) {
					src = new ScreenShootProcess(new InputProcess(src),
							new Area(col - 1, 0, (int) src.size().width - col + 1, (int) src.size().height)).process();
				}
				return src;
			}
		}
		return src;
	}

	/**
	 * 去图片 右边黑色部分
	 * 
	 * @param src
	 * @return
	 */
	public static Mat clearBlackR(Mat src, int min) {
		boolean flag = false;
		for (int col = src.cols() - 1; col > 0; col--) {
			flag = false;
			int index = 0;
			for (int row = 0; row < src.rows(); row++) {
				double px = src.get(row, col)[0];
				if (px > 100) {
					index++;
					if (index == min) {
						flag = true;
						break;
					}
				}
			}
			if (flag) {
				if (col < src.size().width - 2) {
					src = new ScreenShootProcess(new InputProcess(src),
							new Area(0, 0, col + 1, (int) src.size().height)).process();
				}
				break;
			}
		}
		return src;
	}

	/**
	 * 计算区域白点百分比
	 * 
	 * @param mat
	 * @param area
	 * @return
	 */
	public static double calculationArea(Mat mat, Area area) {
		return calculationArea(screenShoot(mat, area));
	}

	public static int calculationArea(Mat mat) {
		double sum = 0;
		double index = 0;
		for (int row = 0; row < mat.rows(); row++) {
			for (int col = 0; col < mat.cols(); col++) {
				double b = mat.get(row, col)[0];
				sum++;
				if (b > 120)
					index++;
			}
		}
		return (int) (index / sum * 100);
	}

	/** 抬头和内容分割坐标 */
	protected static List<Point> localtions = new ArrayList<Point>();

	public static List<Point> initLocaltion(Mat mat, Point point, int direction) {
		localtions.clear();
		for (int i = 0; i <= point.x; i++) {
			Point point1 = new Point(i, point.y);
			localtions.add(point1);
		}

		crawler(mat, point, direction);
		return localtions;
	}

	/**
	 * 得到抬头信息
	 * 
	 * @param point
	 */
	private static void crawler(Mat mat, Point point, int direction) {
		int row = point.y;
		int col = point.x;

		if (row >= mat.rows() - 1 || col >= mat.cols() - 1 || row <= 0) {
			return;
		}
		if (col <= 0) {
			Point point1 = localtions.get(localtions.size() - 1);
			Point point2 = new Point(point1.x + 1, point1.y);
			localtions.add(point2);
			crawler(mat, point2, direction);
		}

		int pex = (int) mat.get(row, col)[0];
		if (pex == 0) {
			Point point1 = new Point(col + 2, row);
			Point point2 = new Point(col + 1, row);
			localtions.add(point2);
			localtions.add(point1);
			crawler(mat, point1, direction);
		}

		if (pex != 0) {
			if ((int) mat.get(row + direction, col)[0] == 0) {
				Point point1 = new Point(col, row + direction);
				localtions.add(point1);
				crawler(mat, point1, direction);
			} else {
				// Point point1 = new Point(col , row + (1 * direction));
				Point point1 = new Point(col + 1, row + direction);
				crawler(mat, point1, direction);
			}
		}
	}

	/**
	 * 截图
	 * 
	 * @param img
	 * @param area
	 * @return
	 */
	public static Mat screenShoot(Mat img, Area area) {
		Mat src = null;
		if (area != null) {
			ScreenShootProcess ssp = new ScreenShootProcess(new InputProcess(img), area);
			src = ssp.process();
		}
		return src;
	}

	/**
	 * 二值化 简化方法
	 * 
	 * @param mat
	 * @param blockSize
	 * @param constValue
	 * @return
	 */
	public static Mat bin(Mat mat, int blockSize, int constValue) {
		if (mat.channels() == 1) {
			return new ThresholdProcess(new InputProcess(mat), blockSize, constValue).process();
		} else {
			Mat g = new GrayProcess(mat).process();
			try {
				return new ThresholdProcess(g, blockSize, constValue).process();
			} finally {
				Mats.releseMat(g);
			}
		}
	}

	/**
	 * 将灰度图自适应二值化
	 * 
	 * @param gray
	 * @return
	 */
	public static Mat adaptiveBin(Mat shard) {
		Mat gray = null;
		try {
			if (shard.channels() != 1) {
				gray = new GrayProcess(shard).process();
			} else {
				gray = shard.clone();
			}
			int GRAY_LEV = 16;
			int GRAY_MIN = (int) (gray.cols() * gray.rows() / GRAY_LEV * 0.02);
			// 每一阶的点数量
			int[] levelPoints = new int[GRAY_LEV];
			for (int i = 0; i < gray.rows(); i++) {
				for (int j = 0; j < gray.cols(); j++) {
					int d = (int) gray.get(i, j)[0];
					int level = d / GRAY_LEV;
					levelPoints[level] += 1;
				}
			}

			int range = 0;
			for (int i = 0; i < levelPoints.length; i++) {
				if (levelPoints[i] > GRAY_MIN) {
					range++;
				}
			}
			int threshold = 5;
			if (range <= 6)
				threshold = 5;
			else if (range == 7)
				threshold = 10;
			else if (range == 8 || range == 9)
				threshold = 20;
			else if (range == 10 || range == 11)
				threshold = 25;
			else if (range >= 12)
				threshold = 30;
			Mat bin = Mats.bin(gray, 15, threshold);
			return bin;
		} finally {
			Mats.releseMat(gray);
		}

	}

	/**
	 * 缩放任意比例简化函数
	 * 
	 * @param mat
	 * @param size
	 * @return
	 */
	public static Mat resize(Mat mat, Size size) {
		return new ResizeProcessAny(new InputProcess(mat), size).process();
	}

	public static boolean isMat(Mat mat) {
		if (mat == null || mat.height() == 0 || mat.width() == 0 || mat.dataAddr() == 0) {
			return false;
		}
		return true;
	}

	public static Mat resizeA(Mat ss, Size size) {
		int targetWidth = (int) size.width - 2;
		int targetHeight = (int) size.height - 2;

		int w = ss.cols();
		int h = ss.rows();
		int offset = 0;
		if (w > h + 1) {
			h = h * targetWidth / w;
			if (h != 0) {
				Mat shard = Mats.resize(ss, new Size(targetWidth, h));
				offset = targetHeight - h;
				int t;
				int b;
				if (offset % 2 == 0) {
					t = offset / 2;
					b = offset / 2;
				} else {
					t = offset / 2 + 1;
					b = offset / 2;
				}
				Imgproc.copyMakeBorder(shard, shard, t + 1, b + 1, 1, 1, Imgproc.BORDER_CONSTANT, new Scalar(0));
				return shard;
			}
		} else if (w + 1 < h) {
			w = w * targetHeight / h;
			if (w != 0) {
				Mat shard = Mats.resize(ss, new Size(w, targetHeight));
				offset = targetWidth - w;
				int l;
				int r;
				if (offset % 2 == 0) {
					l = offset / 2;
					r = offset / 2;
				} else {
					l = offset / 2 + 1;
					r = offset / 2;
				}
				Imgproc.copyMakeBorder(shard, shard, 1, 1, l + 1, r + 1, Imgproc.BORDER_CONSTANT, new Scalar(0));
				return shard;
			}
		}
		Mat shard = Mats.resize(ss, new Size(targetWidth, targetWidth));
		Imgproc.copyMakeBorder(shard, shard, 1, 1, 1, 1, Imgproc.BORDER_CONSTANT, new Scalar(0));
		return shard;
	}

	/**
	 * 自适应参数二值化
	 * 
	 * @param mat
	 * @param min
	 * @param blockSize
	 * @param constValue
	 * @return
	 */
	public static Mat intensifyBin(Mat mat, int min, int blockSize, int constValue) {
		if (mat.channels() != 1) {
			mat = new GrayProcess(mat).process();
		}
		Mat result = new Mat();

		Imgproc.equalizeHist(mat, result);

		for (int row = 0; row < result.rows(); row++) {
			for (int col = 0; col < result.cols(); col++) {
				int pex = (int) result.get(row, col)[0];
				if (pex >= min) {
					result.put(row, col, Mats.PEX_WHITE);
				}
			}
		}

		result = bin(result, blockSize, constValue);

		return result;
	}

	/**
	 * 自适应参数二值化
	 * 
	 * @param gray
	 * @param blockSize
	 * @param constVal
	 * @return
	 */
	public static Mat autoBin(Mat gray, int blockSize, int constVal) {
		if (gray.channels() != 1) {
			gray = new GrayProcess(gray).process();
		}
		int GRAY_LEV = 16;
		// 每一阶的点数量
		int[] levelPoints = new int[GRAY_LEV];
		for (int i = 0; i < gray.rows(); i++) {
			for (int j = 0; j < gray.cols(); j++) {
				int d = (int) gray.get(i, j)[0];
				int level = d / GRAY_LEV;
				levelPoints[level] += 1;
			}
		}

		// 统计灰阶分布选择阈值调整
		int threshold = selectThreshold(levelPoints);

		threshold = (threshold - 10) * 2;

		Mat dst = bin(gray, blockSize, constVal - threshold);

		return dst;
	}

	private static int selectThreshold(int[] levelPoints) {
		int max = 0;
		int index = 0;
		for (int i = 1; i < levelPoints.length; i++) {
			int offset = levelPoints[i] - levelPoints[i - 1];
			if (offset > max) {
				max = offset;
				index = i - 1;
			}
		}
		return index;
	}

	/**
	 * 相对Area 转为 绝对Area
	 * 
	 * @param area
	 * @param areaChild
	 * @return
	 */
	public static Area eavlArea(Area areaParent, Area areaChild) {
		Area area = new Area(areaParent.getX() + areaChild.getX(), areaParent.getY() + areaChild.getY(),
				areaChild.getWidth(), areaChild.getHeight());
		return area;
	}

	/**
	 * 合并
	 * 
	 * @param area1
	 *            左边的区域
	 * @param area2
	 *            右边的区域
	 * @return
	 */
	public static Area mergerArea(Area area, Area finalArea) {
		DomainImpl di = new DomainImpl();
		List<Node> nodes = new ArrayList<Node>();
		// 左上角
		nodes.add(new Node(area.getY(), area.getX()));
		// 右上角
		nodes.add(new Node(area.getY(), area.getX() + area.getWidth()));
		// 左下角
		nodes.add(new Node(area.getY() + area.getHeight(), area.getX()));
		// 右下角
		nodes.add(new Node(area.getY() + area.getHeight(), area.getX() + area.getWidth()));

		// 左上角
		nodes.add(new Node(finalArea.getY(), finalArea.getX()));
		// 右上角
		nodes.add(new Node(finalArea.getY(), finalArea.getX() + finalArea.getWidth()));
		// 左下角
		nodes.add(new Node(finalArea.getY() + finalArea.getHeight(), finalArea.getX()));
		// 右下角
		nodes.add(new Node(finalArea.getY() + finalArea.getHeight(), finalArea.getX() + finalArea.getWidth()));

		di.nodes.addAll(nodes);

		return di.getArea();
	}

	/**
	 * 得到连续白点超过number的行号
	 * 
	 * @param img
	 * @param numer
	 * @return
	 */
	public static List<Integer> getLineRowsContinue(Mat img, int numer) {
		List<Integer> rows = new ArrayList<Integer>();
		// 得到水平的直线
		for (int row = 1; row < img.rows() - 1; row++) {
			double newPex = Mats.PEX_BLACK;
			double oldPex = Mats.PEX_BLACK;
			// 记录连续的白点数量
			int continuous = 0;
			for (int col = 0; col < img.cols(); col++) {
				// 方案2
				if (img.get(row, col)[0] == Mats.PEX_WHITE) {
					newPex = Mats.PEX_WHITE;
				} else {
					newPex = Mats.PEX_BLACK;
				}

				if (newPex == oldPex && newPex == Mats.PEX_WHITE) {
					continuous++;
				} else {
					continuous = 0;
				}

				if (continuous >= numer) {
					rows.add(row);
					break;
				}
				oldPex = newPex;
			}
		}

		return rows;
	}

	/**
	 * 连续白点超过number的列号
	 * 
	 * @param img
	 * @param numer
	 * @return
	 */
	public static List<Integer> getLineColsContinue(Mat img, int numer) {
		List<Integer> cols = new ArrayList<Integer>();
		// 得到水平的直线
		for (int col = 1; col < img.cols() - 1; col++) {
			double newPex = Mats.PEX_BLACK;
			double oldPex = Mats.PEX_BLACK;
			// 记录连续的白点数量
			int continuous = 0;
			for (int row = 0; row < img.rows(); row++) {

				if (img.get(row, col)[0] == Mats.PEX_WHITE) {
					newPex = Mats.PEX_WHITE;
				} else {
					newPex = Mats.PEX_BLACK;
				}

				if (newPex == oldPex && newPex == Mats.PEX_WHITE) {
					continuous++;
				} else {
					continuous = 0;
				}

				if (continuous >= numer) {
					cols.add(col);
					break;
				}
				oldPex = newPex;
			}
		}

		return cols;
	}

	/**
	 * 判断area是否包含(col,row)
	 * 
	 * @param area
	 * @param col
	 * @param row
	 * @return
	 */
	public static boolean containArea(Area area, int col, int row) {
		int x = area.getX();
		int y = area.getY();
		int width = area.getWidth();
		int height = area.getHeight();

		if (x <= col && col <= x + width && y <= row && row <= height + y) {
			return true;
		}

		return false;
	}

	/**
	 * 只写出3通道图片
	 * 
	 * @param filepath
	 * @param format
	 *            格式 .JPG
	 * @param mat
	 */
	public static void imwrite(String filepath, String format, Mat mat) {
		MatOfByte mob = new MatOfByte();
		Highgui.imencode(format, mat, mob);
		byte[] byteArray = mob.toArray();
		Files.write(new File(filepath), byteArray);
	}

	/**
	 * 写入文件，允许中文路径
	 * 
	 * @param filepath
	 * @param mat
	 */
	public static void imwrite(String filepath, Mat mat) {
		String suf = null;
		try {
			suf = filepath.substring(filepath.lastIndexOf("."), filepath.length());
		} catch (Exception e) {
			suf = ".jpg";
		}
		imwrite(filepath, suf, mat);
	}

	/**
	 * 写入文件，允许中文路径
	 * 
	 * @param filepath
	 * @param mat
	 */
	public static void imwrite(File file, Mat mat) {
		imwrite(file.getAbsolutePath(), mat);
	}

	/**
	 * 只返回3通道的mat
	 * 
	 * @param filepath
	 * @param mtype
	 * @return
	 */
	public static Mat imread(String filepath) throws IOException {
		BufferedImage image = null;
		image = ImageIO.read(new File(filepath));

		if (image == null) {
			return new Mat();
		}

		if (image.getType() != BufferedImage.TYPE_3BYTE_BGR) {
			BufferedImage tmp = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
			Graphics2D g = tmp.createGraphics();
			try {
				g.setComposite(AlphaComposite.Src);
				g.drawImage(image, 0, 0, null);
			} finally {
				g.dispose();
			}
			image = tmp;
		}

		byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
		Mat mat = Mat.eye(image.getHeight(), image.getWidth(), CvType.CV_8UC3);
		mat.put(0, 0, pixels);
		return mat;
	}

	public static float cal(Mat img1, Mat img2) {
		float img1_temps[] = Mats.genSimpleMatArray(img1);
		float img2_temps[] = Mats.genSimpleMatArray(img2);
		return cal(img1_temps, img2_temps);
	}

	public static float cal(float[] f1, float[] f2) {
		float dot = 0;
		for (int i = 0; i < f1.length; i++) {
			dot += f1[i] * f2[i];
		}

		float a1 = 0;
		for (float d : f1) {
			a1 += d * d;
		}

		a1 = (float) Math.sqrt(a1);

		float a2 = 0;
		for (float d : f2) {
			a2 += d * d;
		}
		a2 = (float) Math.sqrt(a2);

		if (a1 * a2 == 0) {
			return 0;
		}

		float result = dot / (a1 * a2);

		return result;
	}

	/**
	 * 是否mat资源
	 * 
	 * @param mat
	 * @return
	 */
	public static boolean releseMat(Mat mat) {
		if (null != mat && mat.dataAddr() != 0) {
			mat.release();
			return true;
		}
		return false;
	}

	public static boolean releseMat(Mat... mats) {
		for (Mat mat : mats) {
			releseMat(mat);
		}
		return true;
	}

	/**
	 * area1是否包含area2
	 * 
	 * @param area1
	 * @param area2
	 * @return
	 */
	public static boolean coincidence(Area area1, Area area2) {
		int x1 = Math.max(area1.getX(), area2.getX());
		int y1 = Math.max(area1.getY(), area2.getY());
		int x2 = Math.min(area1.getX() + area1.getWidth(), area2.getX() + area2.getWidth());
		int y2 = Math.min(area1.getY() + area1.getHeight(), area2.getY() + area2.getHeight());

		if (x2 > x1 && y2 > y1) {
			return true;
		}
		return false;
	}

	/**
	 * 合并集合里面的区域
	 * 
	 * @param temps
	 * @return
	 */
	public static Area mergerArea(List<Area> temps) {
		if (null == temps) {
			return null;
		}

		if (temps.size() < 2) {
			return temps.iterator().hasNext() ? temps.iterator().next() : null;
		}

		DomainImpl di = new DomainImpl();
		List<Node> nodes = new ArrayList<Node>();
		for (Area area : temps) {
			// 左上角
			nodes.add(new Node(area.getY(), area.getX()));
			// 左下角
			nodes.add(new Node(area.getY() + area.getHeight(), area.getX()));
			// 右上角
			nodes.add(new Node(area.getY(), area.getX() + area.getWidth()));
			// 右下角
			nodes.add(new Node(area.getY() + area.getHeight(), area.getX() + area.getWidth()));
		}
		di.nodes = nodes;

		return di.getArea();
	}

	/**
	 * 合并2个area，得到都包含它们的area
	 * 
	 * @param area1
	 * @param area2
	 * @return
	 */
	public static Area addArea(Area area1, Area area2) {
		int x = Math.min(area1.getX(), area2.getX());
		int y = Math.min(area1.getY(), area2.getY());
		int x1 = Math.max(area1.getX() + area1.getWidth(), area2.getX() + area2.getWidth());
		int y1 = Math.max(area1.getY() + area1.getHeight(), area2.getY() + area2.getHeight());

		return new Area(x, y, Math.abs(x1 - x), Math.abs(y1 - y));
	}

	/**
	 * 去除全部重合的区域
	 * 
	 * @param areas
	 */
	public static void coincide(List<Area> areas) {
		List<Area> temp = new ArrayList<Area>();
		temp.addAll(areas);
		for (int i = 0; i < temp.size(); i++) {
			Area curArea = temp.get(i);

			Iterator<Area> it = areas.iterator();
			while (it.hasNext()) {
				Area area = it.next();
				if (curArea.equals(area)) {
					continue;
				}

				Area merge = addArea(curArea, area);
				if (merge.getArea() == curArea.getArea()) {
					it.remove();
				}

			}

		}
	}

	public static Mat getBin(Mat shard, int addWidth, int addHeight) {
		Mat temp = Mat.ones(new Size(shard.cols() / addWidth, shard.rows() / addHeight), shard.type());
		int y = 0;
		for (int row = 0; row <= shard.rows()
				- (shard.rows() % addHeight == 0 ? 1 : (shard.rows() % addHeight)); row += addHeight) {
			int x = 0;
			for (int col = 0; col <= shard.cols()
					- (shard.cols() % addWidth == 0 ? 1 : (shard.cols() % addWidth)); col += addWidth) {
				int arrays[][] = new int[addHeight][addWidth];
				int leftUpX = col;
				int leftUpY = row;
				int rightDownX = col + addWidth - 1;
				int rightDownY = row + addHeight - 1;
				initArray(shard, arrays, leftUpY, leftUpX, rightDownY, rightDownX);
				// getIndex
				int index = 0;
				for (int as[] : arrays) {
					for (int t : as) {
						if (t > 100) {
							index++;
						}
					}
				}

				if (index > 1) {
					temp.put(y, x++, Mats.PEX_WHITE);
				} else {
					temp.put(y, x++, Mats.PEX_BLACK);
				}
			}
			y++;
		}
		return temp;
	}

	private static void initArray(Mat src, int[][] a, int leftUpHeight, int leftUpWidth, int rightDownHeight,
			int rightDownWidth) {
		int i = 0;
		for (int y = leftUpHeight; y <= rightDownHeight; y++) {
			int j = 0;
			for (int x = leftUpWidth; x <= rightDownWidth; x++) {
				if (y < 0 || x < 0 || y >= src.rows() || x >= src.cols()) {
					a[i][j++] = -1;
				} else {
					a[i][j++] = (int) src.get(y, x)[0];
				}
			}
			i++;
		}
	}

	public interface AreaFilter {
		boolean doit(Area area);
	}

	public static synchronized List<Area> findContours(Mat mat, AreaFilter filter) {
		Mat bin = mat.clone();
		List<Area> areas = new ArrayList<Area>();
		try {
			List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
			Mat hierarchy = new Mat();
			Imgproc.findContours(bin, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_NONE);
			Mats.releseMat(hierarchy);
			for (MatOfPoint mp : contours) {
				try {
					Rect rect = Imgproc.boundingRect(mp);
					// 过滤一下区域
					Area area = new Area().toAreaByRect(rect);
					// 过滤条件
					if (filter.doit(area)) {
						continue;
					}
					areas.add(area);
				} finally {
					Mats.releseMat(mp);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			releseMat(bin);
		}
		return areas;
	}

	public static List<Area> findContours(Mat mat) {
		return findContours(mat, new AreaFilter() {
			@Override
			public boolean doit(Area area) {
				return area.getArea() < 10 ? true : false;
			}
		});
	}

	public static Mat compression(Mat mat, double maxWidth) {
		double scale = 1;

		int width = mat.width();

		if (width != maxWidth) {
			scale = maxWidth / width;
		} else {
			return mat;
		}

		return resize(mat, new Size(mat.width() * scale, mat.height() * scale));
	}

	public static Mat bin(Mat mat, int blackSize, int constVal, int min) {
		Mat bin = bin(mat, blackSize, constVal);
		if (min > 0) {
			List<Area> minAreas = findContoursByMin(bin, min);
			for (Area area : minAreas) {
				int x = area.getX();
				int y = area.getY();
				int width = area.getWidth();
				int height = area.getHeight();
				Core.rectangle(bin, new org.opencv.core.Point(x, y), new org.opencv.core.Point(x + width, y + height),
						new Scalar(0, 0, 0), -1);
			}
		}
		return bin;
	}

	public static List<Area> findContoursByMin(Mat mat, final int min) {

		return findContours(mat, new AreaFilter() {
			@Override
			public boolean doit(Area area) {
				return area.getArea() < min ? false : true;
			}
		});
	}

	public static List<List<Area>> split(List<Area> areas, SplitCondition sc) {
		List<List<Area>> listAreas = new ArrayList<List<Area>>();
		for (int i = 0; i < areas.size(); i++) {
			Area area1 = areas.get(i);
			if (area1 == null) {
				continue;
			}
			List<Area> listArea = new ArrayList<Area>();
			listArea.add(area1);
			areas.set(i, null);

			for (int j = 0; j < areas.size(); j++) {
				if (j == i) {
					continue;
				}
				Area area2 = areas.get(j);
				if (area2 == null) {
					continue;
				}

				if (sc.toDo(area1, area2)) {
					continue;
				}
				areas.set(j, null);
				listArea.add(area2);
			}
			if (!listArea.isEmpty()) {
				Collections.sort(listArea, new Comparator<Area>() {
					@Override
					public int compare(Area o1, Area o2) {
						return o1.getX() > o2.getX() ? 1 : -1;
					}
				});
				listAreas.add(listArea);
			}
		}
		return listAreas;

	}

	public static List<List<Area>> splitByRows(List<Area> areas) {
		return split(areas, new SplitCondition() {
			@Override
			public boolean toDo(Area area1, Area area2) {
				int height1 = area1.getHeight();
				int height2 = area2.getHeight();
				if (height1 > height2 && height2 * 2.5 < height1) {
					return true;
				}
				if (height2 > height1 && height1 * 2.5 < height2) {
					return true;
				}
				int y1 = area1.getY();
				int y2 = area2.getY();
				int center = y2 + height2 / 2;
				if (!(y1 <= center && center <= y1 + height1)) {
					return true;
				}
				return false;
			}
		});
	}

	public static List<Area> merge(List<Area> areas, int poorMin) {
		if (Lang.isEmpty(areas)) {
			return areas;
		}

		if (areas.size() == 1) {
			return areas;
		}

		Iterator<Area> it = areas.iterator();
		while (it.hasNext()) {
			Area area = it.next();
			if (area == null) {
				it.remove();
			}
		}

		for (int i = 1; i < areas.size(); i++) {
			Area area1 = areas.get(i - 1);
			Area area2 = areas.get(i);
			int poor = Math.abs(area2.getX() - area1.getX() - area1.getWidth());
			if (poor <= poorMin) {
				areas.set(i - 1, Mats.mergerArea(area1, area2));
				areas.set(i, null);
				merge(areas, poorMin);
			}
		}

		return areas;
	}

	public static List<Area> toAreas(List<Integer> rows, List<Integer> cols) {
		List<Area> areas = new ArrayList<Area>();
		for (int i = 1; i < rows.size(); i += 2) {
			int row1 = rows.get(i - 1);
			int row2 = rows.get(i);
			for (int j = 1; j < cols.size(); j += 2) {
				int col1 = cols.get(j - 1);
				int col2 = cols.get(j);
				Area area = new Area(col1, row1, col2 - col1, row2 - row1);

				if (area.getArea() <= 0) {
					continue;
				}

				areas.add(area);
			}
		}
		return areas;
	}

	public static void changePexs(Mat gary, Mat bin, int p) {
		for (int row = 0; row < bin.rows(); row++) {
			for (int col = 0; col < bin.cols(); col++) {
				int pex = (int) gary.get(row, col)[0];
				if (pex > p) {
					bin.put(row, col, Mats.PEX_BLACK);
				}
			}
		}
	}

	public static Mat getMinPexByMat(Mat mat) {
		if (mat.channels() != 3) {
			return mat;
		}

		Mat gray = new Mat(mat.size(), CvType.CV_8UC1);
		for (int row = 0; row < mat.rows(); row++) {
			for (int col = 0; col < mat.cols(); col++) {
				double pexs[] = mat.get(row, col);
				gray.put(row, col, Math.max(Math.max(pexs[0], pexs[1]), pexs[2]));
			}
		}

		return gray;

	}

	public static Mat getRMat(Mat mat) {
		return _getMatRGB(mat, 2);
	}

	public static Mat getGMat(Mat mat) {
		return _getMatRGB(mat, 1);
	}

	public static Mat getBMat(Mat mat) {
		return _getMatRGB(mat, 0);
	}

	private static Mat _getMatRGB(Mat mat, int type) {
		List<Mat> mats = new ArrayList<Mat>();
		Core.split(mat, mats);
		Mat gary = mats.get(type);
		return gary;
	}

}
